Sections

Abstract

In this document, I want to provide a write-up of how this model is similar to and different from the versions previously implemented. This model is meant to provide assembly of multiple sites at the same time, that may, or may not, be connected in some fashion. Along the way, I will mention some of the input and output format that I am expect as documentation. At the end, I will be presenting some preliminary results. I especially want to use this as a vehicle to think about how to analyse those results.

Functions

library(dplyr)     # Data Manipulation
library(ggplot2)   # 2-D Plot
library(plotly)    # 3-D Plot
library(ggfortify) # used for biplots of PCAs
library(vegan)     # Ecological analysis mega-package
# library(RMTRCode2)

# https://stackoverflow.com/a/7172832
ifrm <- function(obj, env = globalenv()) {
    obj <- deparse(substitute(obj))
    if(exists(obj, envir = env)) {
        rm(list = obj, envir = env)
    }
}

Results, No Spatial Structure

First, we load up the preliminary results. In this case, we are loading a system in which 34 basal species and 66 consumer species form the pool for 10 unconnected environments. The pool and interaction matrix were assembled with the default parameters from Law and Morton’s 1996 work.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-None.RData")
)

Events

In total, 9320 events were used in these environments, with the species and environment invasion both randomly assigned. The number of arrival and extinction events were controlled to both be half of this number. We chose this number due to the coupon collecting problem. In particular, we use the result that the probability of encountering each species is bounded: \[\text{Pr}(\text{Draws} < n \log_{e} n + c n) \rightarrow \exp(-\exp(-c)) \text{ as } n \rightarrow \infty\] where \(n\) is the number of species and \(c\) is a constant. For our purposes, we choose \(c = 5\) so that we have a probability of about \(99.3\%\) of seeing each species in each environment. In practice, we failed to observe 2 species-environment combinations. Notably, nearly every species had at least one successful invasion; 72 did not.

The initial abundance was set to be 4000 times the elimination threshold, in line with work on minimum viable populations (Traill et al. 2007). The elimination threshold is admittedly more arbitrary, since it sets an effective individual-area relationship. For this calculation, I set it to \(10^{-4}\), in line with our previous calculations. This is large enough to avoid numerical difficulties from precision, while being low enough to represent a decent sized region.

Since each population is assembling simultaneously, I chose to use exponential waiting times for the inter-species arrival and extinction times. Note that these rates are shared between species and environments, but arrival and extinction are fully independent of each other. Species and environment affected in each event was chosen uniformly at random. The question then is how to set the rate.

To set the rate in this case, I chose to set it to the largest eigenvalue magnitude of the per-environment interaction matrices. This magnitude corresponds to the strongest response we can see from the interaction matrix and, if the interaction matrix is a good approximation for the Jacobian around a stable fixed point (which is not guaranteed), indicates the characteristic time scale of the decay to equilibrium. Hence, (overall) arrival rates and (overall) extinction rates should happen on the same timescale as the (largest) dynamics in the system. Since there are 10 environments, we should then expect that 10 characteristic time scales, on average, should occur in between arrival events in the same environment.

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[, c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)

Every vertical line is a species introduction or extinction by neutral dynamics.

Alpha Diversity plots

Intro

Perhaps more intriguing might be some sense of the biodiversity that we have in each system. We break the abundance results up by environment, then calculate the number of non-zero abundance curves at each time point. We also calculate the Shannon entropy (reminder: higher entropy means more uncertainty which means a flatter distribution).

Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Richness = mean(Richness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

So richness hovers around a similar regime throughout the majority of the simulation. Note in this plot that we have emphasised one environmental curve and superimposed the mean in black. We do manage to reach heights of 11 species in one environment, but these heights are shortlived. Instead, we seem to observe a (time and environment averaged) value of 8.3677307. (If we consider the first 10,000 time units as burn-in, we instead see a value of 8.2536852.)

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

Entropy has a similar, but highly erratic, behaviour. If one tries to follow one of the entropy curves, then one sees that they have fairly substantial periods of almost smooth behaviour followed by suddenly very noisy behaviour, and noise seems to be the dominant mode if one tries to examine the low alpha environment in the background. There are some easy to make predictions. Extinctions reduce the entropy in the system, as you become more certain about what remains. Analogously, arrivals increase the entropy. We can probably better see the relationship beyond these principles by plotting entropy against richness and connecting observations by environment and time.

Entropy-Richness
# ggplot2::ggplot(
#   Diversity %>% dplyr::filter(Environment < 4), 
#   ggplot2::aes(
#     x = Richness,
#     y = Entropy,
#     group = Environment,
#     color = Time
#   )
# ) + ggplot2::geom_path(
# ) + ggplot2::guides(
#   alpha = "none"
# )

plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))

It seems quite hard to tell, but there does not appear to be any particular orientation (clockwise, counter-clockwise) or similar pattern here.

Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

Evenness helps highlight that this is a high variance process but with a relatively constrained mean.

Environment Diversity

We can, of course, flip the idea on its head. Instead of examining the diversity of species within environments, we can look at the diversity of environments occupied by species. Since very few species end up occupying environments, we just look at richness. Unfortunately, this is quite a memory exhaustive task.

EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

One immediately interesting trend here is that very few species are present across more than 5 environments at a given time. Indeed, only 2, 4, 5, 12, 13, 14, 22, 24, 29, 30, 32, 33, 35, 37, 43, 44, 47, 55, 57, 61, 68, 69, 71, 72, 76, 77, 78, 86 are ever present in more than 5 environments at once. We can also examine how long these periods occur for by species by tabulation. We block times so that entries are all of the same unit length, and round the average richness during the given time unit.

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))

Beta Diversity

Principal Components Analysis

(For the desktop, the amount of data we generated is too much, so we need to reduce the amount of data we use. To do so we average over time blocks, here of length 100.)

AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)

We can perform a PCA and see if are data can be summarised by a small number of dimensions. As we shall see, constraining to the first 25 principal components does not harm the system much.

PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)

There is not actually a lot of dependence within the system it appears. Consider the amount of variance explained by the first six principal components (ordered, as is tradition, by amount of variation explained).

head(summary(PCA)$importance[2, ])

The amount of explained variation is as follows.

sum(summary(PCA)$importance[2, ])

The traditional biplot follows, but despite the seeming presence of patterns, so little of the variance is explained that is probably not worth further examination.

ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

We can try again with presence-absence data instead.

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])

So it is a bit better, but not by much.

ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

I should note that this is not a failure of the method persay. What this says to me is that dimension reduction of the system cannot reduce the system to a human-readable set of descriptors. The system is still being reduced (from 100 Species * 10 Environments to 25 Components to cover

sum(summary(PCA)$importance[2, ])

of the variation). My initial hypothesis for when the systems are coupled is that, as coupling increases, the number of principal components should decrease, since one system’s changes will be better predictors of the next system’s changes. (An interesting related question; do the number of principal components correlate with the amount of biodiversity in the system?

ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time

Since trying to reduce the system dimensionally does not work (well enough) a next attempt might be to consider how the pair-wise beta diversity changes over the course of the simulation. Initial problems include that there are quite a few measures of beta diversity as well as that the (square of the) number of environments will determine how many entries we need to consider.

To try this, we are going to reorganise our data into data frame with attributes of environment, time, and traits/species.

Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time

One perhaps interesting idea is to try to group the environments by cluster by considering the abundances (or presence-absence) of the species present as traits. The question then is whether the environments have a tendency to attract to specific points or if they wander around each other without relation. (The latter is more neutral.) If they appear to be convergent (which so far would seem to disagree mostly with the alpha diversity analyses), then that would imply that dynamics determine the majority of the system’s fate, while if they instead wander more randomly, then they would appear to be dominated by the neutral mechanisms. (Of course, this is affected by the rate of the neutral mechanisms, so it exists on a definite continuum.) (Note, of course, that cluster analysis usually includes something like PCA, so we would not necessarily expect a different result.)

My working hypothesis for how to use the distance measures is that we need a metric measure (Kindt and Coe, 2005) with any identification scrubbed off (site, time collected). Kindt and Coe suggest that taking the square root of Bray-Curtis makes it metric and thus usable for mathematical distance analyses (such as hierarchical clustering). The vegan package recomends using Jaccard instead of Bray-Curtis, for the same reason.

EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

Note that are major breakdowns extremely early yielding a large number of clusters but there are expected to be 10 environments (if historical contingency mattered most) or a small number of clusters (if they are converging). The large number of clusters says to me that the neutral dynamics are jumping rapidly from cluster to cluster.

We can try again with presence absence instead.

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

Results, Line Spatial Structure

We repeat the procedures above for the linear set. We use the same pool and interaction matrices, but change the space so that ``adjacent’’ communities can disperse (e.g. 1 <-> 2 <-> 3 <->…<-> 9 <-> 10). Similarly, the history is the same in terms of arrivals and extinctions.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-Line.RData")
)

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[, c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)

Every vertical line is a species introduction or extinction by neutral dynamics.

Alpha Diversity plots

Intro
Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Richness = mean(Richness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1109 row(s) containing missing values (geom_path).
Warning: Removed 104 row(s) containing missing values (geom_path).

Entropy-Richness
plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))
Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1440 row(s) containing missing values (geom_path).
Warning: Removed 136 row(s) containing missing values (geom_path).

Environment Diversity
EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7     8     9    10
    1   80793     0     0     0     0     0     0     0     0     0     0
    2   80623     0     0     0     0     0     0     1     0     0   169
    3   80793     0     0     0     0     0     0     0     0     0     0
    4   80047     0     0     0     0     0     0     0     2     0   744
    5   79899     0     0     0     0     0     1     1     0     0   892
    6   80793     0     0     0     0     0     0     0     0     0     0
    7   80793     0     0     0     0     0     0     0     0     0     0
    8   80793     0     0     0     0     0     0     0     0     0     0
    9   80793     0     0     0     0     0     0     0     0     0     0
    10  80793     0     0     0     0     0     0     0     0     0     0
    11  80793     0     0     0     0     0     0     0     0     0     0
    12    680     0     0     0     0     0     0     0     0     0 80113
    13  80638     0     0     0     0     0     1     0     0     0   154
    14     84     0     0     0     0     0     0     0     0     0 80709
    15  80793     0     0     0     0     0     0     0     0     0     0
    16  80793     0     0     0     0     0     0     0     0     0     0
    17  80793     0     0     0     0     0     0     0     0     0     0
    18  80793     0     0     0     0     0     0     0     0     0     0
    19  80793     0     0     0     0     0     0     0     0     0     0
    20  80793     0     0     0     0     0     0     0     0     0     0
    21  80793     0     0     0     0     0     0     0     0     0     0
    22     62     0     0     0     0     0     0     0     0     0 80731
    23  80793     0     0     0     0     0     0     0     0     0     0
    24  80545     0     0     0     0     1     0     0     0     0   247
    25  80793     0     0     0     0     0     0     0     0     0     0
    26  80793     0     0     0     0     0     0     0     0     0     0
    27  80793     0     0     0     0     0     0     0     0     0     0
    28  80793     0     0     0     0     0     0     0     0     0     0
    29  80720     1     0     0     0     0     0     0     0     0    72
    30  69383     0     1     1     1     0     0     2     1     2 11402
    31  80793     0     0     0     0     0     0     0     0     0     0
    32    500     0     0     0     0     0     0     0     0     1 80292
    33  66478     0     0     1     3     4     1     1     0     2 14303
    34  80793     0     0     0     0     0     0     0     0     0     0
    35  80331     0     0     0     0     0     0     0     2     0   460
    36  80793     0     0     0     0     0     0     0     0     0     0
    37  46276     0     0     2     0     0     0     0     0     1 34514
    38  80793     0     0     0     0     0     0     0     0     0     0
    39  80793     0     0     0     0     0     0     0     0     0     0
    40  80793     0     0     0     0     0     0     0     0     0     0
    41  80793     0     0     0     0     0     0     0     0     0     0
    42  80793     0     0     0     0     0     0     0     0     0     0
    43  27626     0     0     1     0     2     0     1     1     0 53162
    44  60221     0     0     0     0     3     1     0     0     3 20565
    45  80793     0     0     0     0     0     0     0     0     0     0
    46  80793     0     0     0     0     0     0     0     0     0     0
    47  52181     0     0     1     0     1     1     0     0     0 28609
    48  80793     0     0     0     0     0     0     0     0     0     0
    49  80793     0     0     0     0     0     0     0     0     0     0
    50  80793     0     0     0     0     0     0     0     0     0     0
    51  80793     0     0     0     0     0     0     0     0     0     0
    52  80793     0     0     0     0     0     0     0     0     0     0
    53  80793     0     0     0     0     0     0     0     0     0     0
    54  80793     0     0     0     0     0     0     0     0     0     0
    55  78733     0     1     1     1     0     0     0     1     1  2055
    56  80793     0     0     0     0     0     0     0     0     0     0
    57  79019     0     0     0     0     0     0     0     0     0  1774
    58  80793     0     0     0     0     0     0     0     0     0     0
    59  80793     0     0     0     0     0     0     0     0     0     0
    60  80793     0     0     0     0     0     0     0     0     0     0
    61  80465     0     0     0     0     0     0     1     0     0   327
    62  80793     0     0     0     0     0     0     0     0     0     0
    63  80793     0     0     0     0     0     0     0     0     0     0
    64  80793     0     0     0     0     0     0     0     0     0     0
    65  80793     0     0     0     0     0     0     0     0     0     0
    66  80793     0     0     0     0     0     0     0     0     0     0
    67  80793     0     0     0     0     0     0     0     0     0     0
    68  79018     0     1     0     1     0     0     1     2     4  1766
    69  78129     0     1     0     0     2     0     2     1     0  2658
    70  80793     0     0     0     0     0     0     0     0     0     0
    71   1394     0     0     0     0     0     0     0     0     0 79399
    72    292     0     0     1     0     0     0     0     0     0 80500
    73  80793     0     0     0     0     0     0     0     0     0     0
    74  80793     0     0     0     0     0     0     0     0     0     0
    75  80793     0     0     0     0     0     0     0     0     0     0
    76  78672     1     0     0     0     1     0     1     0     1  2117
    77  79764     0     0     0     0     0     0     0     0     0  1029
    78  79252     0     1     0     1     0     0     0     0     0  1539
    79  80793     0     0     0     0     0     0     0     0     0     0
    80  80793     0     0     0     0     0     0     0     0     0     0
    81  80793     0     0     0     0     0     0     0     0     0     0
    82  80793     0     0     0     0     0     0     0     0     0     0
    83  80793     0     0     0     0     0     0     0     0     0     0
    84  80793     0     0     0     0     0     0     0     0     0     0
    85  80793     0     0     0     0     0     0     0     0     0     0
    86  64857     0     0     1     1     3     0     1     1     1 15928
    87  80793     0     0     0     0     0     0     0     0     0     0
    88  80793     0     0     0     0     0     0     0     0     0     0
    89  80793     0     0     0     0     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0     0     0     0
 [ reached getOption("max.print") -- omitted 10 rows ]

Beta Diversity

Principal Components Analysis
AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.19145 0.11089 0.10844 0.07943 0.05993 0.04530 
sum(summary(PCA)$importance[2, ])
[1] 0.99996
ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.21745 0.12196 0.08092 0.05942 0.05217 0.04471 
ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

sum(summary(PCA)$importance[2, ])
[1] 0.99996
ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time
Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time
EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

LS0tDQp0aXRsZTogIk11bHRpcGxlIE51bWVyaWNhbCBBc3NlbWJseSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQojIFNlY3Rpb25zIHsudGFic2V0fQ0KDQojIyBBYnN0cmFjdA0KDQpJbiB0aGlzIGRvY3VtZW50LCBJIHdhbnQgdG8gcHJvdmlkZSBhIHdyaXRlLXVwIG9mIGhvdyB0aGlzIG1vZGVsIGlzIHNpbWlsYXIgdG8gYW5kIGRpZmZlcmVudCBmcm9tIHRoZSB2ZXJzaW9ucyBwcmV2aW91c2x5IGltcGxlbWVudGVkLg0KVGhpcyBtb2RlbCBpcyBtZWFudCB0byBwcm92aWRlIGFzc2VtYmx5IG9mIG11bHRpcGxlIHNpdGVzIGF0IHRoZSBzYW1lIHRpbWUsIHRoYXQgbWF5LCBvciBtYXkgbm90LCBiZSBjb25uZWN0ZWQgaW4gc29tZSBmYXNoaW9uLiANCkFsb25nIHRoZSB3YXksIEkgd2lsbCBtZW50aW9uIHNvbWUgb2YgdGhlIGlucHV0IGFuZCBvdXRwdXQgZm9ybWF0IHRoYXQgSSBhbSBleHBlY3QgYXMgZG9jdW1lbnRhdGlvbi4NCkF0IHRoZSBlbmQsIEkgd2lsbCBiZSBwcmVzZW50aW5nIHNvbWUgcHJlbGltaW5hcnkgcmVzdWx0cy4NCkkgZXNwZWNpYWxseSB3YW50IHRvIHVzZSB0aGlzIGFzIGEgdmVoaWNsZSB0byB0aGluayBhYm91dCBob3cgdG8gYW5hbHlzZSB0aG9zZSByZXN1bHRzLg0KDQojIyBGdW5jdGlvbnMNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikgICAgICMgRGF0YSBNYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAjIDItRCBQbG90DQpsaWJyYXJ5KHBsb3RseSkgICAgIyAzLUQgUGxvdA0KbGlicmFyeShnZ2ZvcnRpZnkpICMgdXNlZCBmb3IgYmlwbG90cyBvZiBQQ0FzDQpsaWJyYXJ5KHZlZ2FuKSAgICAgIyBFY29sb2dpY2FsIGFuYWx5c2lzIG1lZ2EtcGFja2FnZQ0KIyBsaWJyYXJ5KFJNVFJDb2RlMikNCg0KIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNzE3MjgzMg0KaWZybSA8LSBmdW5jdGlvbihvYmosIGVudiA9IGdsb2JhbGVudigpKSB7DQogICAgb2JqIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShvYmopKQ0KICAgIGlmKGV4aXN0cyhvYmosIGVudmlyID0gZW52KSkgew0KICAgICAgICBybShsaXN0ID0gb2JqLCBlbnZpciA9IGVudikNCiAgICB9DQp9DQpgYGANCg0KDQojIyBSZXN1bHRzLCBObyBTcGF0aWFsIFN0cnVjdHVyZSB7LnRhYnNldH0NCg0KRmlyc3QsIHdlIGxvYWQgdXAgdGhlIHByZWxpbWluYXJ5IHJlc3VsdHMuDQpJbiB0aGlzIGNhc2UsIHdlIGFyZSBsb2FkaW5nIGEgc3lzdGVtIGluIHdoaWNoIA0KMzQgYmFzYWwgc3BlY2llcyBhbmQgNjYgY29uc3VtZXIgc3BlY2llcyBmb3JtIHRoZSBwb29sIGZvciAxMCB1bmNvbm5lY3RlZCBlbnZpcm9ubWVudHMuDQpUaGUgcG9vbCBhbmQgaW50ZXJhY3Rpb24gbWF0cml4IHdlcmUgYXNzZW1ibGVkIHdpdGggdGhlIGRlZmF1bHQgcGFyYW1ldGVycyBmcm9tIExhdyBhbmQgTW9ydG9uJ3MgMTk5NiB3b3JrLg0KDQpgYGB7cn0NCmxvYWQoZmlsZS5wYXRoKA0KICAiLi4iLCAiZXhwZXJpbWVudHMiLCAiTU5BLUZpcnN0QXR0ZW1wdC1SZXN1bHQtRW52MTAtTm9uZS5SRGF0YSIpDQopDQpgYGANCg0KIyMjIEV2ZW50cw0KSW4gdG90YWwsIGByIG5yb3cocmVzdWx0JEV2ZW50cylgIGV2ZW50cyB3ZXJlIHVzZWQgaW4gdGhlc2UgZW52aXJvbm1lbnRzLCB3aXRoIHRoZSBzcGVjaWVzIGFuZCBlbnZpcm9ubWVudCBpbnZhc2lvbiBib3RoIHJhbmRvbWx5IGFzc2lnbmVkLg0KVGhlIG51bWJlciBvZiBhcnJpdmFsIGFuZCBleHRpbmN0aW9uIGV2ZW50cyB3ZXJlIGNvbnRyb2xsZWQgdG8gYm90aCBiZSBoYWxmIG9mIHRoaXMgbnVtYmVyLg0KV2UgY2hvc2UgdGhpcyBudW1iZXIgZHVlIHRvIHRoZSBjb3Vwb24gY29sbGVjdGluZyBwcm9ibGVtLg0KSW4gcGFydGljdWxhciwgd2UgdXNlIHRoZSByZXN1bHQgdGhhdCBbdGhlIHByb2JhYmlsaXR5IG9mIGVuY291bnRlcmluZyBlYWNoIHNwZWNpZXMgaXMgYm91bmRlZDpdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvdXBvbl9jb2xsZWN0b3IlMjdzX3Byb2JsZW0jRXh0ZW5zaW9uc19hbmRfZ2VuZXJhbGl6YXRpb25zKQ0KJCRcdGV4dHtQcn0oXHRleHR7RHJhd3N9IDwgbiBcbG9nX3tlfSBuICsgYyBuKSBccmlnaHRhcnJvdyBcZXhwKC1cZXhwKC1jKSkgXHRleHR7IGFzIH0gbiBccmlnaHRhcnJvdyBcaW5mdHkkJA0Kd2hlcmUgJG4kIGlzIHRoZSBudW1iZXIgb2Ygc3BlY2llcyBhbmQgJGMkIGlzIGEgY29uc3RhbnQuDQpGb3Igb3VyIHB1cnBvc2VzLCB3ZSBjaG9vc2UgJGMgPSA1JCBzbyB0aGF0IHdlIGhhdmUgYSBwcm9iYWJpbGl0eSBvZiBhYm91dCAkOTkuM1wlJCBvZiBzZWVpbmcgZWFjaCBzcGVjaWVzIGluIGVhY2ggZW52aXJvbm1lbnQuIA0KSW4gcHJhY3RpY2UsIHdlIGZhaWxlZCB0byBvYnNlcnZlIGByIHN1bSh3aXRoKHJlc3VsdCRFdmVudHMsIHRhYmxlKFNwZWNpZXMsIEVudmlyb25tZW50KSkgPT0gMClgIHNwZWNpZXMtZW52aXJvbm1lbnQgY29tYmluYXRpb25zLg0KTm90YWJseSwgbmVhcmx5IGV2ZXJ5IHNwZWNpZXMgaGFkIGF0IGxlYXN0IG9uZSBzdWNjZXNzZnVsIGludmFzaW9uOw0KYHIgc3VtKHJvd1N1bXMod2l0aChyZXN1bHQkRXZlbnRzICU+JSBmaWx0ZXIoVHlwZSA9PSAiQXJyaXZhbCIpLCB0YWJsZShTcGVjaWVzLCBFbnZpcm9ubWVudCwgU3VjY2VzcykpWywgLCAyXSkgPT0gMClgIGRpZCBub3QuDQoNClRoZSBpbml0aWFsIGFidW5kYW5jZSB3YXMgc2V0IHRvIGJlIDQwMDAgdGltZXMgdGhlIGVsaW1pbmF0aW9uIHRocmVzaG9sZCwgaW4gbGluZSB3aXRoIHdvcmsgb24gbWluaW11bSB2aWFibGUgcG9wdWxhdGlvbnMgKFRyYWlsbCBldCBhbC4gMjAwNykuDQpUaGUgZWxpbWluYXRpb24gdGhyZXNob2xkIGlzIGFkbWl0dGVkbHkgbW9yZSBhcmJpdHJhcnksIHNpbmNlIGl0IHNldHMgYW4gZWZmZWN0aXZlIGluZGl2aWR1YWwtYXJlYSByZWxhdGlvbnNoaXAuDQpGb3IgdGhpcyBjYWxjdWxhdGlvbiwgSSBzZXQgaXQgdG8gJDEwXnstNH0kLCBpbiBsaW5lIHdpdGggb3VyIHByZXZpb3VzIGNhbGN1bGF0aW9ucy4NClRoaXMgaXMgbGFyZ2UgZW5vdWdoIHRvIGF2b2lkIG51bWVyaWNhbCBkaWZmaWN1bHRpZXMgZnJvbSBwcmVjaXNpb24sIHdoaWxlIGJlaW5nIGxvdyBlbm91Z2ggdG8gcmVwcmVzZW50IGEgZGVjZW50IHNpemVkIHJlZ2lvbi4NCg0KU2luY2UgZWFjaCBwb3B1bGF0aW9uIGlzIGFzc2VtYmxpbmcgc2ltdWx0YW5lb3VzbHksIEkgY2hvc2UgdG8gdXNlIGV4cG9uZW50aWFsIHdhaXRpbmcgdGltZXMgZm9yIHRoZSBpbnRlci1zcGVjaWVzIGFycml2YWwgYW5kIGV4dGluY3Rpb24gdGltZXMuDQpOb3RlIHRoYXQgdGhlc2UgcmF0ZXMgYXJlIHNoYXJlZCBiZXR3ZWVuIHNwZWNpZXMgYW5kIGVudmlyb25tZW50cywgYnV0IGFycml2YWwgYW5kIGV4dGluY3Rpb24gYXJlIGZ1bGx5IGluZGVwZW5kZW50IG9mIGVhY2ggb3RoZXIuDQpTcGVjaWVzIGFuZCBlbnZpcm9ubWVudCBhZmZlY3RlZCBpbiBlYWNoIGV2ZW50IHdhcyBjaG9zZW4gdW5pZm9ybWx5IGF0IHJhbmRvbS4NClRoZSBxdWVzdGlvbiB0aGVuIGlzIGhvdyB0byBzZXQgdGhlIHJhdGUuDQoNClRvIHNldCB0aGUgcmF0ZSBpbiB0aGlzIGNhc2UsIEkgY2hvc2UgdG8gc2V0IGl0IHRvIHRoZSBsYXJnZXN0IGVpZ2VudmFsdWUgbWFnbml0dWRlIG9mIHRoZSBwZXItZW52aXJvbm1lbnQgaW50ZXJhY3Rpb24gbWF0cmljZXMuDQpUaGlzIG1hZ25pdHVkZSBjb3JyZXNwb25kcyB0byB0aGUgc3Ryb25nZXN0IHJlc3BvbnNlIHdlIGNhbiBzZWUgZnJvbSB0aGUgaW50ZXJhY3Rpb24gbWF0cml4IGFuZCwgaWYgdGhlIGludGVyYWN0aW9uIG1hdHJpeCBpcyBhIGdvb2QgYXBwcm94aW1hdGlvbiBmb3IgdGhlIEphY29iaWFuIGFyb3VuZCBhIHN0YWJsZSBmaXhlZCBwb2ludCAod2hpY2ggaXMgbm90IGd1YXJhbnRlZWQpLCBpbmRpY2F0ZXMgdGhlIGNoYXJhY3RlcmlzdGljIHRpbWUgc2NhbGUgb2YgdGhlIGRlY2F5IHRvIGVxdWlsaWJyaXVtLg0KSGVuY2UsIChvdmVyYWxsKSBhcnJpdmFsIHJhdGVzIGFuZCAob3ZlcmFsbCkgZXh0aW5jdGlvbiByYXRlcyBzaG91bGQgaGFwcGVuIG9uIHRoZSBzYW1lIHRpbWVzY2FsZSBhcyB0aGUgKGxhcmdlc3QpIGR5bmFtaWNzIGluIHRoZSBzeXN0ZW0uDQpTaW5jZSB0aGVyZSBhcmUgMTAgZW52aXJvbm1lbnRzLCB3ZSBzaG91bGQgdGhlbiBleHBlY3QgdGhhdCAxMCBjaGFyYWN0ZXJpc3RpYyB0aW1lIHNjYWxlcywgb24gYXZlcmFnZSwgc2hvdWxkIG9jY3VyIGluIGJldHdlZW4gYXJyaXZhbCBldmVudHMgaW4gdGhlIHNhbWUgZW52aXJvbm1lbnQuDQoNCiMjIyBBYnVuZGFuY2Ugey50YWJzZXR9DQoNCldpdGggMTAgZW52aXJvbm1lbnRzLCBpdCBpcyBwcm9iYWJseSBub3QgaGVscGZ1bCB0byBjaGVjayAxMCBpbmRpdmlkdWFsIGFidW5kYW5jZSBjdXJ2ZXMsIGJ1dCBsb29raW5nIGF0IHRoZSBmaXJzdCBvbmUgbWlnaHQgYmUgaGVscGZ1bC4NCmBgYHtyfQ0KTGF3TW9ydG9uMTk5Nl9QbG90QWJ1bmRhbmNlKHJlc3VsdCRBYnVuZGFuY2VbLCBjKDEsIDI6MTAxKV0pIC0+IG9iajsNCmBgYA0KYGBge3J9DQpvYmogKyBnZ3Bsb3QyOjpzY2FsZV95X2xvZzEwKCkgKyBnZ3Bsb3QyOjpndWlkZXMoY29sb3IgPSBGQUxTRSkNCmBgYA0KRXZlcnkgdmVydGljYWwgbGluZSBpcyBhIHNwZWNpZXMgaW50cm9kdWN0aW9uIG9yIGV4dGluY3Rpb24gYnkgbmV1dHJhbCBkeW5hbWljcy4NCg0KIyMjIyBBbHBoYSBEaXZlcnNpdHkgcGxvdHMgey50YWJzZXR9DQoNCiMjIyMjIEludHJvDQpQZXJoYXBzIG1vcmUgaW50cmlndWluZyBtaWdodCBiZSBzb21lIHNlbnNlIG9mIHRoZSBiaW9kaXZlcnNpdHkgdGhhdCB3ZSBoYXZlIGluIGVhY2ggc3lzdGVtLg0KV2UgYnJlYWsgdGhlIGFidW5kYW5jZSByZXN1bHRzIHVwIGJ5IGVudmlyb25tZW50LCB0aGVuIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIG5vbi16ZXJvIGFidW5kYW5jZSBjdXJ2ZXMgYXQgZWFjaCB0aW1lIHBvaW50Lg0KV2UgYWxzbyBjYWxjdWxhdGUgdGhlIFNoYW5ub24gZW50cm9weSAocmVtaW5kZXI6IGhpZ2hlciBlbnRyb3B5IG1lYW5zIG1vcmUgdW5jZXJ0YWludHkgd2hpY2ggbWVhbnMgYSBmbGF0dGVyIGRpc3RyaWJ1dGlvbikuDQpgYGB7cn0NCkRpdmVyc2l0eSA8LSBsYXBwbHkoDQogIDE6cmVzdWx0JE51bUVudmlyb25tZW50cywNCiAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICBlbnYgPC0gYWJ1bmRbLCAxICsgMTpudW1TcGVjaWVzICsgbnVtU3BlY2llcyAqIChpIC0gMSldDQogICAgcmljaG5lc3MgPC0gcm93U3VtcyhlbnYgIT0gMCkNCiAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICBlbnRyb3B5IDwtIGVudiAvIGFidW5kU3VtDQogICAgZW50cm9weSA8LSAtIGFwcGx5KA0KICAgICAgZW50cm9weSwgTUFSR0lOID0gMSwNCiAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgc3VtKGlmZWxzZSh4ICE9IDAsIHggKiBsb2coeCksIDApKQ0KICAgICAgfSkNCiAgICBzcGVjaWVzIDwtIGFwcGx5KA0KICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICB0b1N0cmluZyh3aGljaCh4ID4gMCkpDQogICAgICB9DQogICAgKQ0KICAgIGRhdGEuZnJhbWUoVGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgUmljaG5lc3MgPSByaWNobmVzcywgDQogICAgICAgICAgICAgICBFbnRyb3B5ID0gZW50cm9weSwNCiAgICAgICAgICAgICAgIFNwZWNpZXMgPSBzcGVjaWVzLA0KICAgICAgICAgICAgICAgRW52aXJvbm1lbnQgPSBpKQ0KICB9LA0KICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQoNCkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKERpdmVyc2l0eSkNCkRpdmVyc2l0eSA8LSBEaXZlcnNpdHkgJT4lIGRwbHlyOjptdXRhdGUoDQogIEV2ZW5uZXNzID0gRW50cm9weSAvIGxvZyhSaWNobmVzcykNCikNCmBgYA0KDQoNCiMjIyMjIFJpY2huZXNzDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBSaWNobmVzcywNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSBtZWFuKFJpY2huZXNzKQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzDQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQpTbyByaWNobmVzcyBob3ZlcnMgYXJvdW5kIGEgc2ltaWxhciByZWdpbWUgdGhyb3VnaG91dCB0aGUgbWFqb3JpdHkgb2YgdGhlIHNpbXVsYXRpb24uDQpOb3RlIGluIHRoaXMgcGxvdCB0aGF0IHdlIGhhdmUgZW1waGFzaXNlZCBvbmUgZW52aXJvbm1lbnRhbCBjdXJ2ZSBhbmQgc3VwZXJpbXBvc2VkIHRoZSBtZWFuIGluIGJsYWNrLg0KV2UgZG8gbWFuYWdlIHRvIHJlYWNoIGhlaWdodHMgb2YgMTEgc3BlY2llcyBpbiBvbmUgZW52aXJvbm1lbnQsIGJ1dCB0aGVzZSBoZWlnaHRzIGFyZSBzaG9ydGxpdmVkLg0KSW5zdGVhZCwgd2Ugc2VlbSB0byBvYnNlcnZlIGEgKHRpbWUgYW5kIGVudmlyb25tZW50IGF2ZXJhZ2VkKSB2YWx1ZSBvZiANCmByIG1lYW4oRGl2ZXJzaXR5JFJpY2huZXNzKWAuDQooSWYgd2UgY29uc2lkZXIgdGhlIGZpcnN0IDEwLDAwMCB0aW1lIHVuaXRzIGFzIGJ1cm4taW4sIHdlIGluc3RlYWQgc2VlIGEgdmFsdWUgb2YNCmByIG1lYW4oRGl2ZXJzaXR5JFJpY2huZXNzW0RpdmVyc2l0eSRUaW1lID4gMTAwMDBdKWAuKQ0KDQojIyMjIyBFbnRyb3B5DQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFbnRyb3B5LA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFbnRyb3B5ID0gbWVhbihFbnRyb3B5KQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHkNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCkVudHJvcHkgaGFzIGEgc2ltaWxhciwgYnV0IGhpZ2hseSBlcnJhdGljLCBiZWhhdmlvdXIuDQpJZiBvbmUgdHJpZXMgdG8gZm9sbG93IG9uZSBvZiB0aGUgZW50cm9weSBjdXJ2ZXMsIHRoZW4gb25lIHNlZXMgdGhhdCB0aGV5IGhhdmUNCmZhaXJseSBzdWJzdGFudGlhbCBwZXJpb2RzIG9mIGFsbW9zdCBzbW9vdGggYmVoYXZpb3VyIGZvbGxvd2VkIGJ5IHN1ZGRlbmx5DQp2ZXJ5IG5vaXN5IGJlaGF2aW91ciwgYW5kIG5vaXNlIHNlZW1zIHRvIGJlIHRoZSBkb21pbmFudCBtb2RlIGlmIG9uZSB0cmllcw0KdG8gZXhhbWluZSB0aGUgbG93IGFscGhhIGVudmlyb25tZW50IGluIHRoZSBiYWNrZ3JvdW5kLg0KVGhlcmUgYXJlIHNvbWUgZWFzeSB0byBtYWtlIHByZWRpY3Rpb25zLg0KRXh0aW5jdGlvbnMgcmVkdWNlIHRoZSBlbnRyb3B5IGluIHRoZSBzeXN0ZW0sIGFzIHlvdSBiZWNvbWUgbW9yZSBjZXJ0YWluIGFib3V0DQp3aGF0IHJlbWFpbnMuDQpBbmFsb2dvdXNseSwgYXJyaXZhbHMgaW5jcmVhc2UgdGhlIGVudHJvcHkuDQpXZSBjYW4gcHJvYmFibHkgYmV0dGVyIHNlZSB0aGUgcmVsYXRpb25zaGlwIGJleW9uZCB0aGVzZSBwcmluY2lwbGVzIGJ5IHBsb3R0aW5nDQplbnRyb3B5IGFnYWluc3QgcmljaG5lc3MgYW5kIGNvbm5lY3Rpbmcgb2JzZXJ2YXRpb25zIGJ5IGVudmlyb25tZW50IGFuZCB0aW1lLg0KDQojIyMjIyBFbnRyb3B5LVJpY2huZXNzDQpgYGB7cn0NCiMgZ2dwbG90Mjo6Z2dwbG90KA0KIyAgIERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDQpLCANCiMgICBnZ3Bsb3QyOjphZXMoDQojICAgICB4ID0gUmljaG5lc3MsDQojICAgICB5ID0gRW50cm9weSwNCiMgICAgIGdyb3VwID0gRW52aXJvbm1lbnQsDQojICAgICBjb2xvciA9IFRpbWUNCiMgICApDQojICkgKyBnZ3Bsb3QyOjpnZW9tX3BhdGgoDQojICkgKyBnZ3Bsb3QyOjpndWlkZXMoDQojICAgYWxwaGEgPSAibm9uZSINCiMgKQ0KDQpwbG90bHk6OnBsb3RfbHkoZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDIpLA0KICAgICAgICAgICAgICAgIHggPSB+UmljaG5lc3MsIHkgPSB+RW50cm9weSwgeiA9IH5UaW1lLCB0eXBlID0gInNjYXR0ZXIzZCIsDQogICAgICAgICAgICAgICAgbW9kZSA9ICJsaW5lcyIsIG9wYWNpdHkgPSAxLCBsaW5lID0gbGlzdChjb2xvciA9IH5UaW1lKSkNCmBgYA0KSXQgc2VlbXMgcXVpdGUgaGFyZCB0byB0ZWxsLCBidXQgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGFueSBwYXJ0aWN1bGFyIG9yaWVudGF0aW9uIChjbG9ja3dpc2UsIGNvdW50ZXItY2xvY2t3aXNlKSBvciBzaW1pbGFyIHBhdHRlcm4gaGVyZS4NCg0KIyMjIyMgRXZlbm5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFdmVubmVzcyA9IG1lYW4oRXZlbm5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCkV2ZW5uZXNzIGhlbHBzIGhpZ2hsaWdodCB0aGF0IHRoaXMgaXMgYSBoaWdoIHZhcmlhbmNlIHByb2Nlc3MgYnV0IHdpdGggYSByZWxhdGl2ZWx5IGNvbnN0cmFpbmVkIG1lYW4uDQoNCiMjIyMjIEVudmlyb25tZW50IERpdmVyc2l0eQ0KV2UgY2FuLCBvZiBjb3Vyc2UsIGZsaXAgdGhlIGlkZWEgb24gaXRzIGhlYWQuDQpJbnN0ZWFkIG9mIGV4YW1pbmluZyB0aGUgZGl2ZXJzaXR5IG9mIHNwZWNpZXMgd2l0aGluIGVudmlyb25tZW50cywgd2UgY2FuDQpsb29rIGF0IHRoZSBkaXZlcnNpdHkgb2YgZW52aXJvbm1lbnRzIG9jY3VwaWVkIGJ5IHNwZWNpZXMuDQpTaW5jZSB2ZXJ5IGZldyBzcGVjaWVzIGVuZCB1cCBvY2N1cHlpbmcgZW52aXJvbm1lbnRzLCB3ZSBqdXN0IGxvb2sgYXQgcmljaG5lc3MuDQpVbmZvcnR1bmF0ZWx5LCB0aGlzIGlzIHF1aXRlIGEgbWVtb3J5IGV4aGF1c3RpdmUgdGFzay4NCg0KYGBge3J9DQpFbnZEaXZlcnNpdHkgPC0gbGFwcGx5KA0KICAgIDE6KChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzKSwNCiAgICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgICAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICAgICAgZW52IDwtIGFidW5kWywgMSArIGkgKyBudW1TcGVjaWVzICogKDE6cmVzdWx0JE51bUVudmlyb25tZW50cyAtIDEpXQ0KICAgICAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgICAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICAgICAgZW52aXJvbm1lbnRzIDwtIGFwcGx5KA0KICAgICAgICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgICAgICAgIH0NCiAgICAgICAgKQ0KICAgICAgICBkYXRhLmZyYW1lKFRpbWUgPSB0aW1lLCANCiAgICAgICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgICAgICBBYnVuZGFuY2UgPSBhYnVuZFN1bSwNCiAgICAgICAgICAgICAgICAgICBTcGVjaWVzID0gaSwNCiAgICAgICAgICAgICAgICAgICBFbnZpcm9ubWVudHMgPSBlbnZpcm9ubWVudHMpDQogICAgfSwNCiAgICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogICAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCkVudkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKEVudkRpdmVyc2l0eSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRW52RGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKFJpY2huZXNzID4gMSksIA0KICBhZXMoeCA9IFRpbWUsIHkgPSBSaWNobmVzcywgY29sb3IgPSBTcGVjaWVzKQ0KKSArIGdlb21fcG9pbnQoDQogIGFscGhhID0gMC4wMSwgc2l6ZSA9IDMNCikgKyBndWlkZXMoDQogIGNvbG9yID0gIm5vbmUiDQopDQpgYGANCg0KT25lIGltbWVkaWF0ZWx5IGludGVyZXN0aW5nIHRyZW5kIGhlcmUgaXMgdGhhdCB2ZXJ5IGZldyBzcGVjaWVzIGFyZSBwcmVzZW50DQphY3Jvc3MgbW9yZSB0aGFuIDUgZW52aXJvbm1lbnRzIGF0IGEgZ2l2ZW4gdGltZS4NCkluZGVlZCwgb25seSBgciB1bmlxdWUoRW52RGl2ZXJzaXR5JFNwZWNpZXNbRW52RGl2ZXJzaXR5JFJpY2huZXNzID4gNV0pYCBhcmUgDQpldmVyIHByZXNlbnQgaW4gbW9yZSB0aGFuIDUgZW52aXJvbm1lbnRzIGF0IG9uY2UuDQpXZSBjYW4gYWxzbyBleGFtaW5lIGhvdyBsb25nIHRoZXNlIHBlcmlvZHMgb2NjdXIgZm9yIGJ5IHNwZWNpZXMgYnkgdGFidWxhdGlvbi4NCldlIGJsb2NrIHRpbWVzIHNvIHRoYXQgZW50cmllcyBhcmUgYWxsIG9mIHRoZSBzYW1lIHVuaXQgbGVuZ3RoLCBhbmQgcm91bmQgdGhlDQphdmVyYWdlIHJpY2huZXNzIGR1cmluZyB0aGUgZ2l2ZW4gdGltZSB1bml0Lg0KDQpgYGB7cn0NCndpdGgoRW52RGl2ZXJzaXR5ICU+JSBtdXRhdGUoDQogIFRpbWUgPSBmbG9vcihUaW1lKQ0KICApICU+JSBncm91cF9ieSgNCiAgICBUaW1lLCBTcGVjaWVzDQogICAgKSAlPiUgc3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSByb3VuZChtZWFuKFJpY2huZXNzKSksDQogICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApLA0KICAgICB0YWJsZShTcGVjaWVzLCBSaWNobmVzcykpDQpgYGANCiMjIyMgQmV0YSBEaXZlcnNpdHkgey50YWJzZXR9DQoNCiMjIyMjIFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzDQooRm9yIHRoZSBkZXNrdG9wLCB0aGUgYW1vdW50IG9mIGRhdGEgd2UgZ2VuZXJhdGVkIGlzIHRvbyBtdWNoLCBzbyB3ZSBuZWVkIHRvDQpyZWR1Y2UgdGhlIGFtb3VudCBvZiBkYXRhIHdlIHVzZS4NClRvIGRvIHNvIHdlIGF2ZXJhZ2Ugb3ZlciB0aW1lIGJsb2NrcywgaGVyZSBvZiBsZW5ndGggMTAwLikNCmBgYHtyfQ0KQXZlcmFnZWRBYnVuZGFuY2UgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KV2UgY2FuIHBlcmZvcm0gYSBQQ0EgYW5kIHNlZSBpZiBhcmUgZGF0YSBjYW4gYmUgc3VtbWFyaXNlZCBieSBhIHNtYWxsIG51bWJlciBvZg0KZGltZW5zaW9ucy4NCkFzIHdlIHNoYWxsIHNlZSwgY29uc3RyYWluaW5nIHRvIHRoZSBmaXJzdCAyNSBwcmluY2lwYWwgY29tcG9uZW50cyBkb2VzIG5vdA0KaGFybSB0aGUgc3lzdGVtIG11Y2guDQpgYGB7cn0NClBDQSA8LSBwcmNvbXAoQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwgDQogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IFRSVUUsIHJhbmsuID0gMjUpDQpgYGANCg0KVGhlcmUgaXMgbm90IGFjdHVhbGx5IGEgbG90IG9mIGRlcGVuZGVuY2Ugd2l0aGluIHRoZSBzeXN0ZW0gaXQgYXBwZWFycy4NCkNvbnNpZGVyIHRoZSBhbW91bnQgb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCBzaXggcHJpbmNpcGFsIGNvbXBvbmVudHMNCihvcmRlcmVkLCBhcyBpcyB0cmFkaXRpb24sIGJ5IGFtb3VudCBvZiB2YXJpYXRpb24gZXhwbGFpbmVkKS4NCg0KYGBge3J9DQpoZWFkKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANCg0KVGhlIGFtb3VudCBvZiBleHBsYWluZWQgdmFyaWF0aW9uIGlzIGFzIGZvbGxvd3MuDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNClRoZSB0cmFkaXRpb25hbCBiaXBsb3QgZm9sbG93cywgYnV0IGRlc3BpdGUgdGhlIHNlZW1pbmcgcHJlc2VuY2Ugb2YgcGF0dGVybnMsDQpzbyBsaXR0bGUgb2YgdGhlIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCB0aGF0IGlzIHByb2JhYmx5IG5vdCB3b3J0aCBmdXJ0aGVyDQpleGFtaW5hdGlvbi4NCg0KYGBge3J9DQpnZ3Bsb3QyOjphdXRvcGxvdChQQ0EsIGxvYWRpbmdzID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpXZSBjYW4gdHJ5IGFnYWluIHdpdGggcHJlc2VuY2UtYWJzZW5jZSBkYXRhIGluc3RlYWQuDQpgYGB7cn0NCkF2ZXJhZ2VkUEEgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICBkcGx5cjo6YWNyb3NzKC5jb2xzID0gIXRpbWUsIC5mbnMgPSB+IC54ID4gMCkNCikgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgdGltZQ0KKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCikNCmBgYA0KDQpgYGB7cn0NClBDQVBBIDwtIHByY29tcChBdmVyYWdlZFBBICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFLCByYW5rLiA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChzdW1tYXJ5KFBDQVBBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANClNvIGl0IGlzIGEgYml0IGJldHRlciwgYnV0IG5vdCBieSBtdWNoLg0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQVBBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpJIHNob3VsZCBub3RlIHRoYXQgdGhpcyBpcyBub3QgYSBmYWlsdXJlIG9mIHRoZSBtZXRob2QgcGVyc2F5LiANCldoYXQgdGhpcyBzYXlzIHRvIG1lIGlzIHRoYXQgZGltZW5zaW9uIHJlZHVjdGlvbiBvZiB0aGUgc3lzdGVtIGNhbm5vdCByZWR1Y2UgdGhlDQpzeXN0ZW0gdG8gYSBodW1hbi1yZWFkYWJsZSBzZXQgb2YgZGVzY3JpcHRvcnMuDQpUaGUgc3lzdGVtIGlzIHN0aWxsIGJlaW5nIHJlZHVjZWQgKGZyb20gMTAwIFNwZWNpZXMgKiAxMCBFbnZpcm9ubWVudHMgdG8gMjUgQ29tcG9uZW50cyB0byBjb3ZlciANCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgIA0Kb2YgdGhlIHZhcmlhdGlvbikuDQo8IS0tIA0KSW5zdGVhZCwgZ2l2ZW4gdGhlIG51bWJlciBvZiBlbnZpcm9ubWVudHMgYW5kIHRoZWlyIGluZGVwZW5kZW5jZSwgdGhpcyBzZWVtcyB0byANCnN1Z2dlc3QgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdGhlIGNoYW5nZSBpbiBlbnZpcm9ubWVudHMgY2FuIGJlIGRlc2NyaWJlZCBieSBhYm91dCB0d28gY29tcG9uZW50cyBwbHVzIHRpbWUgYW5kIHNvbWUgbWlub3IgdmFyaWF0aW9uIHdoZW4gY29uc2lkZXJpbmcgdGhlIHN5c3RlbSBhcyBhIHdob2xlLiANCihUaGlzIGRvZXMgbm90IHdvcmsgZm9yIHRoZSBpbmRpdmlkdWFsIHN5c3RlbXMgc2luY2UgdGhlcmUgaXMgcmVkdW5kYW5jeSB0aGF0IGlzIGxvc3QuKSAtLT4NCk15IGluaXRpYWwgaHlwb3RoZXNpcyBmb3Igd2hlbiB0aGUgc3lzdGVtcyBhcmUgY291cGxlZCBpcyB0aGF0LCBhcyBjb3VwbGluZyBpbmNyZWFzZXMsIHRoZSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgc2hvdWxkIGRlY3JlYXNlLCBzaW5jZSBvbmUgc3lzdGVtJ3MgY2hhbmdlcyB3aWxsIGJlIGJldHRlciBwcmVkaWN0b3JzIG9mIHRoZSBuZXh0IHN5c3RlbSdzIGNoYW5nZXMuDQooQW4gaW50ZXJlc3RpbmcgcmVsYXRlZCBxdWVzdGlvbjsgZG8gdGhlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyBjb3JyZWxhdGUgd2l0aCB0aGUgYW1vdW50IG9mIGJpb2RpdmVyc2l0eSBpbiB0aGUgc3lzdGVtPw0KDQpgYGB7ciBjbGVhbnVwfQ0KaWZybShBdmVyYWdlZEFidW5kYW5jZSkNCmlmcm0oQXZlcmFnZWRQQSkNCmlmcm0oUENBKQ0KaWZybShQQ0FQQSkNCmBgYA0KDQojIyMjIyBCZXRhIERpdmVyc2l0eSBPdmVyIFRpbWUNClNpbmNlIHRyeWluZyB0byByZWR1Y2UgdGhlIHN5c3RlbSBkaW1lbnNpb25hbGx5IGRvZXMgbm90IHdvcmsgKHdlbGwgZW5vdWdoKSBhDQpuZXh0IGF0dGVtcHQgbWlnaHQgYmUgdG8gY29uc2lkZXIgaG93IHRoZSBwYWlyLXdpc2UgYmV0YSBkaXZlcnNpdHkgY2hhbmdlcyBvdmVyDQp0aGUgY291cnNlIG9mIHRoZSBzaW11bGF0aW9uLg0KSW5pdGlhbCBwcm9ibGVtcyBpbmNsdWRlIHRoYXQgdGhlcmUgYXJlIHF1aXRlIGEgZmV3IG1lYXN1cmVzIG9mIGJldGEgZGl2ZXJzaXR5IA0KYXMgd2VsbCBhcyB0aGF0IHRoZSAoc3F1YXJlIG9mIHRoZSkgbnVtYmVyIG9mIGVudmlyb25tZW50cyB3aWxsIGRldGVybWluZSBob3cNCm1hbnkgZW50cmllcyB3ZSBuZWVkIHRvIGNvbnNpZGVyLg0KDQpUbyB0cnkgdGhpcywgd2UgYXJlIGdvaW5nIHRvIHJlb3JnYW5pc2Ugb3VyIGRhdGEgaW50byBkYXRhIGZyYW1lIHdpdGggYXR0cmlidXRlcw0Kb2YgZW52aXJvbm1lbnQsIHRpbWUsIGFuZCB0cmFpdHMvc3BlY2llcy4NCmBgYHtyfQ0KRW52aXJvbm1lbnRzIDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICANCiAgICByZXR2YWwgPC0gZGF0YS5mcmFtZShFbnZpcm9ubWVudCA9IHRvU3RyaW5nKGkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWUgPSB0aW1lDQogICAgKQ0KICAgIA0KICAgIHJldHZhbCA8LSBjYmluZChyZXR2YWwsIGVudikNCiAgICBjb2xuYW1lcyhyZXR2YWwpIDwtIGMoIkVudmlyb25tZW50IiwgIlRpbWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCYXNhbCIsIDE6MzQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJDb25zdW1lciIsIDM1OjEwMCkNCiAgICApDQogICAgcmV0dXJuKHJldHZhbCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCiAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KICApICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgdGltZQ0KICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQogICksDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQpFbnZpcm9ubWVudHMgPC0gZHBseXI6OmJpbmRfcm93cyhFbnZpcm9ubWVudHMpDQpFbnZpcm9ubWVudHMgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6ZmlsdGVyKA0KICBkcGx5cjo6aWZfYW55KGRwbHlyOjpzdGFydHNfd2l0aChjKCJCYXNhbCIsICJDb25zdW1lciIpKSwgfiAoLnggIT0gMCkpLA0KKQ0KYGBgDQoNCiMjIyMjIENsdXN0ZXIgQW5hbHlzaXMgT3ZlciBUaW1lDQpPbmUgcGVyaGFwcyBpbnRlcmVzdGluZyBpZGVhIGlzIHRvIHRyeSB0byBncm91cCB0aGUgZW52aXJvbm1lbnRzIGJ5IGNsdXN0ZXINCmJ5IGNvbnNpZGVyaW5nIHRoZSBhYnVuZGFuY2VzIChvciBwcmVzZW5jZS1hYnNlbmNlKSBvZiB0aGUgc3BlY2llcyBwcmVzZW50IGFzIHRyYWl0cy4NClRoZSBxdWVzdGlvbiB0aGVuIGlzIHdoZXRoZXIgdGhlIGVudmlyb25tZW50cyBoYXZlIGEgdGVuZGVuY3kgdG8gYXR0cmFjdCB0byANCnNwZWNpZmljIHBvaW50cyBvciBpZiB0aGV5IHdhbmRlciBhcm91bmQgZWFjaCBvdGhlciB3aXRob3V0IHJlbGF0aW9uLg0KKFRoZSBsYXR0ZXIgaXMgbW9yZSBuZXV0cmFsLikNCklmIHRoZXkgYXBwZWFyIHRvIGJlIGNvbnZlcmdlbnQgKHdoaWNoIHNvIGZhciB3b3VsZCBzZWVtIHRvIGRpc2FncmVlIG1vc3RseSB3aXRoDQp0aGUgYWxwaGEgZGl2ZXJzaXR5IGFuYWx5c2VzKSwgdGhlbiB0aGF0IHdvdWxkIGltcGx5IHRoYXQgZHluYW1pY3MgZGV0ZXJtaW5lIHRoZQ0KbWFqb3JpdHkgb2YgdGhlIHN5c3RlbSdzIGZhdGUsIHdoaWxlIGlmIHRoZXkgaW5zdGVhZCB3YW5kZXIgbW9yZSByYW5kb21seSwgdGhlbg0KdGhleSB3b3VsZCBhcHBlYXIgdG8gYmUgZG9taW5hdGVkIGJ5IHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMuDQooT2YgY291cnNlLCB0aGlzIGlzIGFmZmVjdGVkIGJ5IHRoZSByYXRlIG9mIHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMsIHNvIGl0IGV4aXN0cw0Kb24gYSBkZWZpbml0ZSBjb250aW51dW0uKQ0KKE5vdGUsIG9mIGNvdXJzZSwgdGhhdCBjbHVzdGVyIGFuYWx5c2lzIHVzdWFsbHkgaW5jbHVkZXMgc29tZXRoaW5nIGxpa2UgUENBLA0Kc28gd2Ugd291bGQgbm90IG5lY2Vzc2FyaWx5IGV4cGVjdCBhIGRpZmZlcmVudCByZXN1bHQuKQ0KDQpNeSB3b3JraW5nIGh5cG90aGVzaXMgZm9yIGhvdyB0byB1c2UgdGhlIGRpc3RhbmNlIG1lYXN1cmVzIGlzIHRoYXQgd2UgbmVlZCBhDQptZXRyaWMgbWVhc3VyZSAoS2luZHQgYW5kIENvZSwgMjAwNSkgd2l0aCBhbnkgaWRlbnRpZmljYXRpb24gc2NydWJiZWQgb2ZmIChzaXRlLA0KdGltZSBjb2xsZWN0ZWQpLg0KS2luZHQgYW5kIENvZSBzdWdnZXN0IHRoYXQgdGFraW5nIHRoZSBzcXVhcmUgcm9vdCBvZiBCcmF5LUN1cnRpcyBtYWtlcyBpdCBtZXRyaWMNCmFuZCB0aHVzIHVzYWJsZSBmb3IgbWF0aGVtYXRpY2FsIGRpc3RhbmNlIGFuYWx5c2VzIChzdWNoIGFzIGhpZXJhcmNoaWNhbCANCmNsdXN0ZXJpbmcpLg0KVGhlIGB2ZWdhbmAgcGFja2FnZSByZWNvbWVuZHMgdXNpbmcgSmFjY2FyZCBpbnN0ZWFkIG9mIEJyYXktQ3VydGlzLCBmb3IgdGhlIHNhbWUNCnJlYXNvbi4NCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0IDwtIGhjbHVzdChFbnZpcm9ubWVudERpc3RhbmNlKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3QsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQpOb3RlIHRoYXQgYXJlIG1ham9yIGJyZWFrZG93bnMgZXh0cmVtZWx5IGVhcmx5IHlpZWxkaW5nIGEgbGFyZ2UgbnVtYmVyIG9mIA0KY2x1c3RlcnMgYnV0IHRoZXJlIGFyZSBleHBlY3RlZCB0byBiZSAxMCBlbnZpcm9ubWVudHMgKGlmIGhpc3RvcmljYWwgY29udGluZ2VuY3kgDQptYXR0ZXJlZCBtb3N0KSBvciBhIHNtYWxsIG51bWJlciBvZiBjbHVzdGVycyAoaWYgdGhleSBhcmUgY29udmVyZ2luZykuDQpUaGUgbGFyZ2UgbnVtYmVyIG9mIGNsdXN0ZXJzIHNheXMgdG8gbWUgdGhhdCB0aGUgbmV1dHJhbCBkeW5hbWljcyBhcmUganVtcGluZw0KcmFwaWRseSBmcm9tIGNsdXN0ZXIgdG8gY2x1c3Rlci4NCg0KV2UgY2FuIHRyeSBhZ2FpbiB3aXRoIHByZXNlbmNlIGFic2VuY2UgaW5zdGVhZC4NCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlUEEgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIsIGJpbmFyeSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3RQQSA8LSBoY2x1c3QoRW52aXJvbm1lbnREaXN0YW5jZVBBKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3RQQSwgbGFiZWxzID0gRkFMU0UpDQpgYGANCg0KPCEtLSMjIyMjIFN0YXRlIE5ldHdvcmsNCkFuIGFkZGl0aW9uYWwgaWRlYSB0aGF0IG1pZ2h0IGJlIHdvcnRoIGFuIGluaXRpYWwgbG9vayBvdmVyIGlzIHdoZXRoZXIgd2UgY2FuDQpjcmVhdGUgdGhlIHN1YnNwYWNlIG9mIHRoZSBncmFwaCBleHBsb3JlZCBieSB0aGUgc2ltdWxhdGlvbiBzZXQuIA0KVGhpcyBpcyBwcm9iYWJseSBvZiB0aGUgbG93ZXN0IHByaW9yaXR5IG9mIHRoZSB0aGluZ3MgZGVzY3JpYmVkIGFib3ZlLCBidXQgdGhpcw0KaXMgb2YgbWF0aGVtYXRpY2FsIGludGVyZXN0IGFuZCBkb2VzIGhhdmUgc29tZSByZWxhdGlvbnNoaXAgdG8gZGl2ZXJzaXR5IA0KbWVhc3VyZXMuDQpTdWNoIGEgbWFwIHdvdWxkIHVzdWFsbHkgYmUgdmlzdWFsaXNlZCB3aXRoIGNvbHVtbnMgcmVwcmVzZW50aW5nIHJpY2huZXNzIGFuZA0KZWRnZXMgcmVwcmVzZW50aW5nIG1vdmVzIGFsb25nIHRoZSBzeXN0ZW0uDQpXZSBjYW4gY29sb3VyIHRoZSBlZGdlcyB0byByZXByZXNlbnQgd2hldGhlciB0aGV5IHdlcmUgdmlhIGFycml2YWwsIGR5bmFtaWMNCmV4dGluY3Rpb24sIG9yIG5ldXRyYWwgZXh0aW5jdGlvbiBhcyB3ZWxsLiAtLT4NCg0KIyMgUmVzdWx0cywgTGluZSBTcGF0aWFsIFN0cnVjdHVyZSB7LnRhYnNldH0NCg0KV2UgcmVwZWF0IHRoZSBwcm9jZWR1cmVzIGFib3ZlIGZvciB0aGUgbGluZWFyIHNldC4NCldlIHVzZSB0aGUgc2FtZSBwb29sIGFuZCBpbnRlcmFjdGlvbiBtYXRyaWNlcywgYnV0IGNoYW5nZSB0aGUgc3BhY2Ugc28gdGhhdA0KYGBhZGphY2VudCcnIGNvbW11bml0aWVzIGNhbiBkaXNwZXJzZSAoZS5nLiAxIDwtPiAyIDwtPiAzIDwtPi4uLjwtPiA5IDwtPiAxMCkuDQpTaW1pbGFybHksIHRoZSBoaXN0b3J5IGlzIHRoZSBzYW1lIGluIHRlcm1zIG9mIGFycml2YWxzIGFuZCBleHRpbmN0aW9ucy4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQtUmVzdWx0LUVudjEwLUxpbmUuUkRhdGEiKQ0KKQ0KYGBgDQoNCiMjIyBBYnVuZGFuY2Ugey50YWJzZXR9DQoNCldpdGggMTAgZW52aXJvbm1lbnRzLCBpdCBpcyBwcm9iYWJseSBub3QgaGVscGZ1bCB0byBjaGVjayAxMCBpbmRpdmlkdWFsIGFidW5kYW5jZSBjdXJ2ZXMsIGJ1dCBsb29raW5nIGF0IHRoZSBmaXJzdCBvbmUgbWlnaHQgYmUgaGVscGZ1bC4NCmBgYHtyfQ0KTGF3TW9ydG9uMTk5Nl9QbG90QWJ1bmRhbmNlKHJlc3VsdCRBYnVuZGFuY2VbLCBjKDEsIDI6MTAxKV0pIC0+IG9iajsNCmBgYA0KYGBge3J9DQpvYmogKyBnZ3Bsb3QyOjpzY2FsZV95X2xvZzEwKCkgKyBnZ3Bsb3QyOjpndWlkZXMoY29sb3IgPSBGQUxTRSkNCmBgYA0KRXZlcnkgdmVydGljYWwgbGluZSBpcyBhIHNwZWNpZXMgaW50cm9kdWN0aW9uIG9yIGV4dGluY3Rpb24gYnkgbmV1dHJhbCBkeW5hbWljcy4NCg0KIyMjIyBBbHBoYSBEaXZlcnNpdHkgcGxvdHMgey50YWJzZXR9DQoNCiMjIyMjIEludHJvDQpgYGB7cn0NCkRpdmVyc2l0eSA8LSBsYXBwbHkoDQogIDE6cmVzdWx0JE51bUVudmlyb25tZW50cywNCiAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICBlbnYgPC0gYWJ1bmRbLCAxICsgMTpudW1TcGVjaWVzICsgbnVtU3BlY2llcyAqIChpIC0gMSldDQogICAgcmljaG5lc3MgPC0gcm93U3VtcyhlbnYgIT0gMCkNCiAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICBlbnRyb3B5IDwtIGVudiAvIGFidW5kU3VtDQogICAgZW50cm9weSA8LSAtIGFwcGx5KA0KICAgICAgZW50cm9weSwgTUFSR0lOID0gMSwNCiAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgc3VtKGlmZWxzZSh4ICE9IDAsIHggKiBsb2coeCksIDApKQ0KICAgICAgfSkNCiAgICBzcGVjaWVzIDwtIGFwcGx5KA0KICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICB0b1N0cmluZyh3aGljaCh4ID4gMCkpDQogICAgICB9DQogICAgKQ0KICAgIGRhdGEuZnJhbWUoVGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgUmljaG5lc3MgPSByaWNobmVzcywgDQogICAgICAgICAgICAgICBFbnRyb3B5ID0gZW50cm9weSwNCiAgICAgICAgICAgICAgIFNwZWNpZXMgPSBzcGVjaWVzLA0KICAgICAgICAgICAgICAgRW52aXJvbm1lbnQgPSBpKQ0KICB9LA0KICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQoNCkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKERpdmVyc2l0eSkNCkRpdmVyc2l0eSA8LSBEaXZlcnNpdHkgJT4lIGRwbHlyOjptdXRhdGUoDQogIEV2ZW5uZXNzID0gRW50cm9weSAvIGxvZyhSaWNobmVzcykNCikNCmBgYA0KDQoNCiMjIyMjIFJpY2huZXNzDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBSaWNobmVzcywNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSBtZWFuKFJpY2huZXNzKQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzDQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQoNCiMjIyMjIEVudHJvcHkNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHksDQogICAgY29sb3IgPSBmYWN0b3IoRW52aXJvbm1lbnQpLA0KICAgIGFscGhhID0gaWZlbHNlKEVudmlyb25tZW50ID09IDEsIDEsIDAuMykNCiAgKQ0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQogIGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICBUaW1lDQogICAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICAgIEVudHJvcHkgPSBtZWFuKEVudHJvcHkpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRW50cm9weQ0KICApLA0KICBjb2xvciA9ICJibGFjayIsDQogIGluaGVyaXQuYWVzID0gRkFMU0UNCikgKyBnZ3Bsb3QyOjpndWlkZXMoDQogIGFscGhhID0gIm5vbmUiDQopICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZGlzY3JldGUoDQogICJFbnZpcm9ubWVudCINCikNCmBgYA0KDQojIyMjIyBFbnRyb3B5LVJpY2huZXNzDQpgYGB7cn0NCnBsb3RseTo6cGxvdF9seShkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKEVudmlyb25tZW50IDwgMiksDQogICAgICAgICAgICAgICAgeCA9IH5SaWNobmVzcywgeSA9IH5FbnRyb3B5LCB6ID0gflRpbWUsIHR5cGUgPSAic2NhdHRlcjNkIiwNCiAgICAgICAgICAgICAgICBtb2RlID0gImxpbmVzIiwgb3BhY2l0eSA9IDEsIGxpbmUgPSBsaXN0KGNvbG9yID0gflRpbWUpKQ0KYGBgDQoNCiMjIyMjIEV2ZW5uZXNzDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFdmVubmVzcywNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgRXZlbm5lc3MgPSBtZWFuKEV2ZW5uZXNzKQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzDQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQoNCiMjIyMjIEVudmlyb25tZW50IERpdmVyc2l0eQ0KDQpgYGB7cn0NCkVudkRpdmVyc2l0eSA8LSBsYXBwbHkoDQogICAgMTooKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMpLA0KICAgIGZ1bmN0aW9uKGksIGFidW5kLCBudW1TcGVjaWVzKSB7DQogICAgICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgICAgICBlbnYgPC0gYWJ1bmRbLCAxICsgaSArIG51bVNwZWNpZXMgKiAoMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzIC0gMSldDQogICAgICAgIHJpY2huZXNzIDwtIHJvd1N1bXMoZW52ICE9IDApDQogICAgICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgICAgICBlbnZpcm9ubWVudHMgPC0gYXBwbHkoDQogICAgICAgICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgICAgICAgICAgdG9TdHJpbmcod2hpY2goeCA+IDApKQ0KICAgICAgICAgICAgfQ0KICAgICAgICApDQogICAgICAgIGRhdGEuZnJhbWUoVGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgICAgIFJpY2huZXNzID0gcmljaG5lc3MsIA0KICAgICAgICAgICAgICAgICAgIEFidW5kYW5jZSA9IGFidW5kU3VtLA0KICAgICAgICAgICAgICAgICAgIFNwZWNpZXMgPSBpLA0KICAgICAgICAgICAgICAgICAgIEVudmlyb25tZW50cyA9IGVudmlyb25tZW50cykNCiAgICB9LA0KICAgIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgICBudW1TcGVjaWVzID0gKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMNCikNCg0KRW52RGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRW52RGl2ZXJzaXR5KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBFbnZEaXZlcnNpdHkgJT4lIGRwbHlyOjpmaWx0ZXIoUmljaG5lc3MgPiAxKSwgDQogIGFlcyh4ID0gVGltZSwgeSA9IFJpY2huZXNzLCBjb2xvciA9IFNwZWNpZXMpDQopICsgZ2VvbV9wb2ludCgNCiAgYWxwaGEgPSAwLjAxLCBzaXplID0gMw0KKSArIGd1aWRlcygNCiAgY29sb3IgPSAibm9uZSINCikNCmBgYA0KDQpgYGB7cn0NCndpdGgoRW52RGl2ZXJzaXR5ICU+JSBtdXRhdGUoDQogIFRpbWUgPSBmbG9vcihUaW1lKQ0KICApICU+JSBncm91cF9ieSgNCiAgICBUaW1lLCBTcGVjaWVzDQogICAgKSAlPiUgc3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSByb3VuZChtZWFuKFJpY2huZXNzKSksDQogICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApLA0KICAgICB0YWJsZShTcGVjaWVzLCBSaWNobmVzcykpDQpgYGANCiMjIyMgQmV0YSBEaXZlcnNpdHkgey50YWJzZXR9DQoNCiMjIyMjIFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzDQpgYGB7cn0NCkF2ZXJhZ2VkQWJ1bmRhbmNlIDwtIHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICB0aW1lDQopICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICBkcGx5cjo6YWNyb3NzKC5mbnMgPSB+IG1lYW4oLngpKQ0KKQ0KYGBgDQoNCmBgYHtyfQ0KUENBIDwtIHByY29tcChBdmVyYWdlZEFidW5kYW5jZSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLCANCiAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSwgcmFuay4gPSAyNSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KDQpgYGB7cn0gDQpzdW0oc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQSwgbG9hZGluZ3MgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBBdmVyYWdlZEFidW5kYW5jZSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLA0KICAgICAgICAgICAgICAgICAgY29sb3VyID0gInRpbWUiKQ0KYGBgDQoNCmBgYHtyfQ0KQXZlcmFnZWRQQSA8LSByZXN1bHQkQWJ1bmRhbmNlICU+JSBkYXRhLmZyYW1lKA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgdGltZSA9IGZsb29yKHRpbWUvMTAwKSoxMDANCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIGRwbHlyOjphY3Jvc3MoLmNvbHMgPSAhdGltZSwgLmZucyA9IH4gLnggPiAwKQ0KKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICB0aW1lDQopICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICBkcGx5cjo6YWNyb3NzKC5mbnMgPSB+IG1lYW4oLngpKQ0KKQ0KYGBgDQoNCmBgYHtyfQ0KUENBUEEgPC0gcHJjb21wKEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwgDQogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IFRSVUUsIHJhbmsuID0gMjUpDQpgYGANCg0KYGBge3J9DQpoZWFkKHN1bW1hcnkoUENBUEEpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQVBBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpgYGB7cn0gDQpzdW0oc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYCANCg0KYGBge3IgY2xlYW51cDJ9DQppZnJtKEF2ZXJhZ2VkQWJ1bmRhbmNlKQ0KaWZybShBdmVyYWdlZFBBKQ0KaWZybShQQ0EpDQppZnJtKFBDQVBBKQ0KYGBgDQoNCiMjIyMjIEJldGEgRGl2ZXJzaXR5IE92ZXIgVGltZQ0KYGBge3J9DQpFbnZpcm9ubWVudHMgPC0gbGFwcGx5KA0KICAxOnJlc3VsdCROdW1FbnZpcm9ubWVudHMsDQogIGZ1bmN0aW9uKGksIGFidW5kLCBudW1TcGVjaWVzKSB7DQogICAgdGltZSA8LSBhYnVuZFssIDFdDQogICAgZW52IDwtIGFidW5kWywgMSArIDE6bnVtU3BlY2llcyArIG51bVNwZWNpZXMgKiAoaSAtIDEpXQ0KICAgIA0KICAgIHJldHZhbCA8LSBkYXRhLmZyYW1lKEVudmlyb25tZW50ID0gdG9TdHJpbmcoaSksDQogICAgICAgICAgICAgICAgICAgICAgICAgVGltZSA9IHRpbWUNCiAgICApDQogICAgDQogICAgcmV0dmFsIDwtIGNiaW5kKHJldHZhbCwgZW52KQ0KICAgIGNvbG5hbWVzKHJldHZhbCkgPC0gYygiRW52aXJvbm1lbnQiLCAiVGltZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIkJhc2FsIiwgMTozNCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIkNvbnN1bWVyIiwgMzU6MTAwKQ0KICAgICkNCiAgICByZXR1cm4ocmV0dmFsKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgfSwNCiAgYWJ1bmQgPSByZXN1bHQkQWJ1bmRhbmNlICU+JSBkYXRhLmZyYW1lKA0KICApICU+JSBkcGx5cjo6bXV0YXRlKA0KICAgIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQogICkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICB0aW1lDQogICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCiAgKSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCkVudmlyb25tZW50cyA8LSBkcGx5cjo6YmluZF9yb3dzKEVudmlyb25tZW50cykNCkVudmlyb25tZW50cyA8LSBFbnZpcm9ubWVudHMgJT4lIGRwbHlyOjpmaWx0ZXIoDQogIGRwbHlyOjppZl9hbnkoZHBseXI6OnN0YXJ0c193aXRoKGMoIkJhc2FsIiwgIkNvbnN1bWVyIikpLCB+ICgueCAhPSAwKSksDQopDQpgYGANCg0KIyMjIyMgQ2x1c3RlciBBbmFseXNpcyBPdmVyIFRpbWUNCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0IDwtIGhjbHVzdChFbnZpcm9ubWVudERpc3RhbmNlKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3QsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KRW52aXJvbm1lbnREaXN0YW5jZVBBIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiLCBiaW5hcnkgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0UEEgPC0gaGNsdXN0KEVudmlyb25tZW50RGlzdGFuY2VQQSkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoRW52RGlzdENsdXN0UEEsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQo=